// IMPORTANT: Read the license included with this code archive.

namespace Hosting
{
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.ComponentModel.Design.Serialization;

    internal class DesignSite : ISite, IServiceProvider, IDictionaryService
    {
        private readonly IDesignerHost host;

        private IComponent component;

        private Hashtable dictionary;

        private string name;

        public DesignSite(IDesignerHost host, string name)
        {
            this.host = host;
            this.name = name;
        }

        #region ISite and IServiceProvider Implementations

        public System.ComponentModel.IComponent Component
        {
            get
            {
                return this.component;
            }
        }

        public System.ComponentModel.IContainer Container
        {
            get
            {
                return this.host.Container;
            }
        }

        public bool DesignMode
        {
            get
            {
                return true;
            }
        }

        public string Name
        {
            get
            {
                return this.name;
            }
            set
            {
                string oldName;

                // Check if we're trying to set a null name
                if (value == null)
                {
                    throw new ArgumentException("Cannot set a component's name to a null value.");
                }

                // Check we're not trying to set the same name as we've already got
                if (value == this.name)
                {
                    return;
                }

                // Make sure there isn't already a component with this name in the container
                if (((DesignerHost)this.host).ContainsName(value))
                {
                    throw new ArgumentException("There is already a component with this name in the container.");
                }

                // Remember the old name
                oldName = this.name;

                // Apply new name
                MemberDescriptor md = TypeDescriptor.CreateProperty(
                    this.component.GetType(), "Name", typeof(string), new Attribute[] { });
                ((DesignerHost)this.host).OnComponentChanging(this.component, md);
                this.name = value;
                ((DesignerHost)this.host).OnComponentRename(this.component, oldName, this.name);
                ((DesignerHost)this.host).OnComponentChanged(this.component, md, oldName, this.name);
            }
        }

        public object GetService(System.Type serviceType)
        {
            if (serviceType == typeof(IDictionaryService))
            {
                return this;
            }
            else
            {
                return this.host.GetService(serviceType);
            }
        }

        #endregion

        internal void SetComponent(IComponent component)
        {
            this.component = component;
            if (this.name == null)
            {
                var nameService = (INameCreationService)this.GetService(typeof(INameCreationService));
                this.name = nameService.CreateName(this.host.Container, component.GetType());
            }
        }

        #region IDictionaryService Implementation

        public object GetKey(object value)
        {
            if (this.dictionary == null)
            {
                return null;
            }
            else
            {
                return this.GetKeyFromValue(value);
            }
        }

        private object GetKeyFromValue(object value)
        {
            IDictionaryEnumerator e = this.dictionary.GetEnumerator();

            while (e.MoveNext())
            {
                if (e.Value == value)
                {
                    return e.Key;
                }
            }

            return null;
        }

        public object GetValue(object key)
        {
            if (this.dictionary == null)
            {
                return null;
            }
            else
            {
                return this.dictionary[key];
            }
        }

        public void SetValue(object key, object value)
        {
            if (this.dictionary == null)
            {
                this.dictionary = new Hashtable();
            }

            // Remove if we're setting to null
            if (value == null)
            {
                this.dictionary.Remove(key);
                return;
            }

            // Set value
            this.dictionary[key] = value;
        }

        #endregion
    }
}